#include "keyboard.h"

using namespace Epsilon;

#include <stdio.h>
#include <string.h> // memset

#include "../STL.h" // eSTL::IterValid

bool
Keyboard::GetKey (Key key) const
{
  switch (key.getType ())
  {
    case Text:
      return text_.getKey (key.getCode ());
      break;
    case Misc:
      return misc_.getKey (key.getCode ());
      break;
    case Modifier:
      return modifier_.getKey (key.getCode ());
      break;
    default:
      return false;
  }
}

void
Keyboard::PressKey (Key key)
{
  SetKey (key, true);

  std::vector <Keyboard_Listener *>::const_iterator listener =
    listeners_.begin ();
  std::vector <Keyboard_Listener *>::const_iterator end =
    listeners_.end   ();

  /* Run through all listeners, and notify them of this key press... */
  while (eSTL::IterValid (listener, end)) {
    /* Allow the listener to prevent delegation... */
    if ((*listener)->OnKeyPress (key))
      return;

    ++listener;
  }

//  printf ("Pressed Key: %x (%s) :: [%s]\n", key.getCode (), getKeyName (key), getKeyTypeName (key));
}

void
Keyboard::ReleaseKey (Key key)
{
  SetKey (key, false);

  std::vector <Keyboard_Listener *>::const_iterator listener =
    listeners_.begin ();
  std::vector <Keyboard_Listener *>::const_iterator end =
    listeners_.end   ();

  /* Run through all listeners, and notify them of this key release... */
  while (eSTL::IterValid (listener, end)) {
    /* Allow the listener to prevent delegation... */
    if ((*listener)->OnKeyRelease (key))
      return;

    ++listener;
  }

//  printf ("Released Key: %x (%s) :: [%s]\n", key.getCode (), getKeyName (key), getKeyTypeName (key));
}


void
Keyboard::SetKey (Key key, bool state)
{
  switch (key.getType ())
  {
    case Text:
      /// Assert (key.getCode () < TextKeys::_NUMBER_OF_KEYS);
      text_.setKey (key.getCode (), state);
      break;
    case Misc:
      /// Assert (key.getCode () < TextKeys::_NUMBER_OF_KEYS);
      misc_.setKey (key.getCode (), state);
      break;
    case Modifier:
      modifier_.setKey (key.getCode (), state);
      break;
    default:
      /// UNKNOWN Key Type!
      break;
  }
}

void
Keyboard::ResetKeys (void)
{
  text_.reset ();
  misc_.reset ();
  modifier_.reset ();
}

const char*
Keyboard::getKeyName (Key key) const
{
  KeyType type = key.getType ();
  KeyCode code = key.getCode ();

  switch (type)
  {
    case Text:
      return text_.getKeyName (code);
      break;
    case Misc:
      return misc_.getKeyName (code);
      break;
    case Modifier:
      return modifier_.getKeyName (code);
      break;
    default:
      return "N/A";
      break;
  }
}

const char*
Keyboard::getKeyTypeName (Key key) const
{
  KeyType type = key.getType ();
  
  switch (type)
  {
    case Text:
      return "Text";
      break;
    case Misc:
      return "Misc";
      break;
    case Modifier:
      return "Modifier";
      break;
    default:
      return "N/A";
      break;
  }
}

const char
Keyboard::getKeyChar (Key key, const ModifierKeys* pModifiers)
{
  bool shift = false;

  if (pModifiers != NULL)
  {
    if (pModifiers->getKey (Keyboard::ModifierKeys::Shift))
      shift = true;
  }

  if (key.getType () == Text)
  {
    if (key.getCode () <= TextKeys::Z)
    {
      if (! shift)
        return 'a' + ((key.getCode () - TextKeys::A));
      else
        return 'A' + ((key.getCode () - TextKeys::A));
    }

    else if (key.getCode () <= TextKeys::Nine)
    {
      if (! shift)
        return '0' + (key.getCode () - TextKeys::Zero);
      else
      {
        switch (key.getCode ())
        {
          case TextKeys::One:
            return '!';
          case TextKeys::Two:
            return '@';
          case TextKeys::Three:
            return '#';
          case TextKeys::Four:
            return '$';
          case TextKeys::Five:
            return '%';
          case TextKeys::Six:
            return '^';
          case TextKeys::Seven:
            return '&';
          case TextKeys::Eight:
            return '*';
          case TextKeys::Nine:
            return '(';
          case TextKeys::Zero:
            return ')';
        }
      }
    }

    else
    {
      switch (key.getCode ())
      {
        case TextKeys::Minus:
          return (shift ? '_' : '-');
        case TextKeys::Equals:
          return (shift ? '+' : '=');
        case TextKeys::Tilde:
        case TextKeys::WHATTHEHELL:
          return (shift ? '~' : '`');
        case TextKeys::BackSlash:
          return (shift ? '|' : '\\');
        case TextKeys::LeftBracket:
          return (shift ? '{' : '[');
        case TextKeys::RightBracket:
          return (shift ? '}' : ']');
        case TextKeys::LeftAngleBracket:
        case TextKeys::Comma:
          return (shift ? '<' : ',');
        case TextKeys::RightAngleBracket:
        case TextKeys::Period:
          return (shift ? '>' : '.');
        case TextKeys::Colon:
        case TextKeys::SemiColon:
          return (shift ? ':' : ';');
        case TextKeys::Quote:
        case TextKeys::DoubleQuote:
          return (shift ? '"' : '\'');
        case TextKeys::Question:
        case TextKeys::Slash:
          return (shift ? '?' : '/');
      }
    }
  }

  return '\0';
}



bool
Keyboard::TextKeys::getKey (KeyCode code) const
{
  /// Assert (code < _NUMBER_OF_KEYS);
  return keys_ [code];
}

void
Keyboard::TextKeys::setKey (KeyCode code, bool state)
{
  /// Assert (code < _NUMBER_OF_KEYS);
  keys_ [code] = state;
}

void
Keyboard::TextKeys::reset (void)
{
  memset (keys_, 0, sizeof (keys_));
}

const char*
Keyboard::TextKeys::getKeyName (KeyCode code) const
{
        return "N/A";
}

bool
Keyboard::MiscKeys::getKey (KeyCode code) const
{
  /// Assert (code < _NUMBER_OF_KEYS);
  return keys_ [code];
}

void
Keyboard::MiscKeys::setKey (KeyCode code, bool state)
{
  /// Assert (code < _NUMBER_OF_KEYS);
  keys_ [code] = state;
}

void
Keyboard::MiscKeys::reset (void)
{
  memset (keys_, 0, sizeof (keys_));
}

const char*
Keyboard::MiscKeys::getKeyName (KeyCode code) const
{
  struct KeyName {
    KeyCode     code;
    const char* name;
  };

  static const KeyName KeyToName [] = {
    { Insert, "Insert" },
    { Delete, "Delete" },
    { Home,   "Home"   },
    { End, "End" },
    { PageUp, "Page Up" },
    { PageDown, "Page Down" },
    { Tab, "Tab" },
    { Up, "Up" },
    { Down, "Down" },
    { Left, "Left" },
    { Right, "Right" },
    { Enter, "Enter" },
    { Space, "Space" },
    { Backspace, "Backspace" },
    { Escape,    "Escape"    },
    { F1, "F1" },
    { F2, "F2" },
    { F3, "F3" },
    { F4, "F4" },
    { F5, "F5" },
    { F6, "F6" },
    { F7, "F7" },
    { F8, "F8" },
    { F9, "F9" },
    { F10, "F10" },
    { F11, "F11" },
    { F12, "F12" },

    { Print, "Print Screen" },
    { ScrollLock, "Scroll Lock" },
    { Pause, "Pause" },

    { NumPad_Equals, "Number Pad: Equals (=)" },
    { NumPad_Period, "Number Pad: Period (.)" },
    { NumPad_Multiply, "Number Pad: Multiply (*)" },
    { NumPad_Minus, "Number Pad: Minus (-)" },
    { NumPad_Plus, "Number Pad: Plus (+)" },
    { NumPad_Divide, "Number Pad: Divide (/)" },
    { NumPad_0, "Number Pad: 0" },
    { NumPad_1, "Number Pad: 1" },
    { NumPad_2, "Number Pad: 2" },
    { NumPad_3, "Number Pad: 3" },
    { NumPad_4, "Number Pad: 4" },
    { NumPad_5, "Number Pad: 5" },
    { NumPad_6, "Number Pad: 6" },
    { NumPad_7, "Number Pad: 7" },
    { NumPad_8, "Number Pad: 8" },
    { NumPad_9, "Number Pad: 9" },
    { NumPad_Enter, "Number Pad: Enter" }
  };

  for (unsigned int i = 0; i < sizeof (KeyToName) / sizeof (KeyName); i++)
  {
    if (KeyToName [i].code == code)
      return KeyToName [i].name;
  }

  return "Unknown";
}

bool
Keyboard::ModifierKeys::getKey (KeyCode code) const
{
  return (mask_ & code) != 0;
}

void
Keyboard::ModifierKeys::setKey (KeyCode code, bool state)
{
/* TODO : Make this a mode, rather than something done at compile-time...

            On OS X, we always have a bitmask available representing all
              pressed modifier keys.

            On Win32, we only receive information when a modifier key is
              pressed or released.
*/
#ifdef __APPLE__
  mask_ = code; /* OS X */
#else
  if (state)
    mask_ |= code;
  else
    mask_ &= ~code;
#endif
}

void
Keyboard::ModifierKeys::reset (void)
{
  mask_ = 0;
}

const char*
Keyboard::ModifierKeys::getKeyName (KeyCode code) const
{
  return "N/A";
}


bool
Keyboard::InstallListener (Keyboard_Listener* pListener)
{
  if (pListener != NULL) {
    listeners_.push_back (pListener);
    return true;
  } else {
    return false;
  }
}

bool
Keyboard::UninstallListener (Keyboard_Listener* pListener)
{
  bool removed = false;

  if (pListener != NULL) {
    std::vector <Keyboard_Listener *>::iterator listener =
        listeners_.begin ();
    std::vector <Keyboard_Listener *>::iterator end =
        listeners_.end ();

    while (eSTL::IterValid (listener, end)) {
      if ((*listener) == pListener) {
        listeners_.erase (listener);
        removed = true;
      }

      ++listener;
    }
  }

  return removed;
}



const Keyboard::ModifierKeys*
Keyboard::getModifierKeys (void) const
{
  return &modifier_;
}



#include <cstdio>

Keyboard::TextKeys::TextKeys (void)
{
  memset (keys_, 0, _NUMBER_OF_KEYS * sizeof (bool));
}

Keyboard::MiscKeys::MiscKeys (void)
{
  memset (keys_, 0, _NUMBER_OF_KEYS * sizeof (bool));
}

Keyboard::ModifierKeys::ModifierKeys (void)
{
  mask_ = 0;
}
